/**
 * 
 */
package edu.umd.clip.lm.model.training.metrics;

import org.w3c.dom.Element;

import edu.umd.clip.lm.model.training.ContextVariable;
import edu.umd.clip.lm.model.training.ContextVariableStats;
import edu.umd.clip.lm.util.ConstCountDistribution;
import edu.umd.clip.lm.util.CountDistribution;
import edu.umd.clip.lm.util.Long2IntMap;
import edu.umd.clip.lm.util.ProbMath;

/**
 * @author Denis Filimonov <den@cs.umd.edu>
 *
 */
public class RelativeReverseConditionalEntropy extends ContextVariableMetrics {

	/**
	 * @param orderContextVariables
	 * @param name
	 */
	public RelativeReverseConditionalEntropy() {
		super("H(x|w) / H(x)");
	}

	/**
	 * @param elem
	 */
	public RelativeReverseConditionalEntropy(Element elem) {
		super(elem);
	}

	/* (non-Javadoc)
	 * @see edu.umd.clip.lm.model.training.ContextVariableMetrics#computeScore(edu.umd.clip.lm.model.training.NewTrainer.ContextVariableStats)
	 */
	@Override
	public double computeScore(ContextVariable ctxVar, ContextVariableStats stat) {
		long totalCount = 0;
		double condEntropy = 0;
		for(Object d : stat.w2xDistributions.values()) {
			ConstCountDistribution dist = (ConstCountDistribution) d;
			
			long counts[] = dist.values();
			for(long count : counts) {
				condEntropy -= count * ProbMath.log2(count);
			}

			long distCount = dist.getTotalCount();
			if (distCount > 0) {
				condEntropy += distCount * ProbMath.log2(distCount);
				totalCount += distCount;
			}
		}
		condEntropy /= totalCount;
		
		double entropy = 0;
		for(long count : stat.contextCounts.values() ) {
			entropy -= count * ProbMath.log2(count);
		}
		entropy = entropy / totalCount + ProbMath.log2(totalCount);
		
		double score = condEntropy / entropy;
		if (Double.isNaN(score)) {
			System.err.printf("computeScore() : totalCount=%d, w2xDistributions.size=%d, contextCounts.size=%d, condEntropy=%g, entropy=%g\n",
					totalCount, stat.w2xDistributions.size(), stat.contextCounts.size(), condEntropy, entropy);			
		}
		return score;
	}

	/* (non-Javadoc)
	 * @see edu.umd.clip.lm.model.training.ContextVariableMetrics#needContextVarCounts()
	 */
	@Override
	public boolean needContextVarCounts() {
		return true;
	}

	/* (non-Javadoc)
	 * @see edu.umd.clip.lm.model.training.ContextVariableMetrics#needVarToWordCounts()
	 */
	@Override
	public boolean needVarToWordCounts() {
		return false;
	}

	/* (non-Javadoc)
	 * @see edu.umd.clip.lm.model.training.ContextVariableMetrics#needWordCounts()
	 */
	@Override
	public boolean needWordCounts() {
		return false;
	}

	/* (non-Javadoc)
	 * @see edu.umd.clip.lm.model.training.ContextVariableMetrics#needWordToVarCounts()
	 */
	@Override
	public boolean needWordToVarCounts() {
		return true;
	}

	@Override
	public String toString() {
		return "RelativeReverseConditionalEntropy []";
	}

}
